package org.jerry.virtualoperator.task;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
import com.github.kevinsawicki.http.HttpRequest;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.jerry.virtualoperator.constant.Status;
import org.jerry.virtualoperator.constant.StringConstant;
import org.jerry.virtualoperator.form.VsmsCreateForm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.http.MediaType;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;
import java.util.stream.Collectors;

@Slf4j
@Component
public class VsmsReportSchedule {

    static ObjectMapper jackson = new ObjectMapper();

    @Autowired
    ThreadPoolTaskExecutor fileReadExecutor;
    @Autowired
    ThreadPoolTaskExecutor mobileReportExecutor;
    @Value("${server.tomcat.basedir}")
    private String basedir;

    private static final int[] PROPORTIONS = {10, 20, 70};
    private static final Status[] STATUSES = {null, Status.failed, Status.success};

    @Scheduled(cron = "0 0/3 * * * ? ")
    public void report() {
        File file = new File(basedir + File.separator + StringConstant.MOBILE_FILE_DIR);
        File[] mobileFiles = file.listFiles();
        if(mobileFiles == null || mobileFiles.length == 0) {
            log.info("no mobile file");
            return;
        }
        for (File mobileFile : mobileFiles) {
            fileReadExecutor.execute(new ReportTask(mobileFile));
        }

    }

    @AllArgsConstructor
    class ReportTask implements Runnable {

        private File file;

        @Override
        public void run() {
            try {
                Status status = randomStatus();
                if(status == null) {
                    return;
                }

                ObjectReader objectReader = jackson.readerFor(VsmsCreateForm.class);
                VsmsCreateForm form = objectReader.readValue(file);
                Map<String, Object> report = new HashMap<>(3);
                report.put("smsId", FilenameUtils.getBaseName(file.getName()));
                report.put("mobiles", form.getMetas().stream().map(VsmsCreateForm.SendMeta::getMobile).collect(Collectors.toList()));
                report.put("status", status.name());
                String body = HttpRequest.post(form.getCallbackUrl())
                        .connectTimeout(5000)
                        .readTimeout(5000)
                        .contentType(MediaType.APPLICATION_JSON_VALUE)
                        .acceptJson()
                        .send(jackson.writeValueAsString(report))
                        .body();

                log.info("reported back: {}", body);
            } catch (Exception e) {
                log.error(e.getLocalizedMessage(), e);
            } finally {
                file.delete();
            }
        }
    }

    @AllArgsConstructor
    class ReportMeta implements Callable<String> {

        private String url;
        private String mobile;
        private String smsId;

        @SneakyThrows
        @Override
        public String call() {

            Map<String, String> report = new HashMap<>(3);
            report.put("smsId", smsId);
            report.put("mobile", mobile);
            report.put("status", "DELIVERED");
            return  HttpRequest.post(url)
                    .connectTimeout(5000)
                    .readTimeout(5000)
                    .contentType(MediaType.APPLICATION_JSON_VALUE)
                    .acceptJson()
                    .send(jackson.writeValueAsString(report))
                    .body();
        }


    }

    private Status randomStatus() {
        int random = ThreadLocalRandom.current().nextInt(100) + 1;

        int start = 1, end = 0;
        for (int i = 0; i < PROPORTIONS.length; i++) {
            end += PROPORTIONS[i];
            if(start <= random && random <= end) {
                return STATUSES[i];
            }
            start += PROPORTIONS[i];
        }

        return null;
    }
}
